package glassline;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import transducer.TChannel;
import transducer.TEvent;
import transducer.TReceiver;
import transducer.Transducer;
import agent.Agent;

import glassline.interfaces.Conveyor;
import glassline.interfaces.Oven;

public class OvenAgent extends Agent implements TReceiver, Oven{
	
	Transducer myTransducer;
	
	public OvenAgent(String name, Transducer t) {
		super();
		this.name = name;
		myTransducer = t;
		t.register(this, TChannel.SENSORS);
		t.register(this, TChannel.GUI);
	}
	

	// Name of the oven
	String name;
	
	//List of glass
	public List<myGlass> glassList = new ArrayList<myGlass>();
	
	//Conveyor Agent
	private Conveyor prevConveyor;
	private Conveyor postConveyor;
	
	//Timer for oven. Determines how long glass will stay in the oven
	Timer timer = new Timer();
	
	//Sensors
	public boolean enterSensor = false;
	public boolean exitSensor = false;
	
	// State of the oven on/off
	boolean isOn = false;
	boolean firstRun = true;
	private boolean postConveyorReady = true;
	
	// State of the glass
	public enum glassState {sensed, baking, finishedBaking, waiting, goingToNextConveyor, sent};
	
	//Private class for glass
	private class myGlass{
		Glass glass;
		public glassState state;
		
		public myGlass(Glass g){
			this.glass = g;
			state = glassState.sensed;
			
		}
		
	}
	
	
	// ----- MESSAGES ------
	
	// Msg from the previous conveyor to give the oven glass
	@Override
	public synchronized void msgHereIsGlass(Glass g) {
		print("Glass received from previous conveyor");
		glassList.add(new myGlass(g));
		stateChanged();
	}
	
	// Msg from the post conveyor that glass can be passed
	@Override
	public synchronized void msgIamDone() {
		postConveyorReady = true;
		//glassList.get(0).state = glassState.goingToNextConveyor;
		stateChanged();
	}
	
	
	
	// ----- SCHEDULER -----
	public boolean pickAndExecuteAnAction() {
		
		for(myGlass mg: glassList){
			if(mg.state == glassState.sensed && enterSensor){
				startOven(mg);
				//this.isOn = true;
				return true;
			}
		}
		
		for(myGlass mg: glassList){
			if(mg.state == glassState.finishedBaking && !(exitSensor) && postConveyorReady){
				sendGlassToNextConveyor(mg);
				firstRun = false;
				return true;
			}
		}
		
		
		for(myGlass mg: glassList){
			if(mg.state == glassState.sent && !exitSensor){
				readyForNextGlass(mg);
				return true;
			}
		}
		
		return false;
	}
		
		
	// ----- ACTIONS -----
	void startOven(final myGlass glass) {
		glass.state = glassState.baking;
		print ("Glass is cooking... for 2 seconds");
		timer.schedule(new TimerTask() {
			public void run(){//this routine is like a message reception    
				myTransducer.fireEvent(TChannel.GUI, TEvent.STARTOVEN, null);
				glass.state = glassState.finishedBaking;
				print("Done baking!");
				myTransducer.fireEvent(TChannel.GUI, TEvent.STOPOVEN, null);
				stateChanged();
			}
		}, 2000);
		glass.state = glassState.finishedBaking;//for unit test
	
	}
	
	void sendGlassToNextConveyor(myGlass glass){
		print("Sending the glass from oven to conveyor");
		postConveyor.msgHereIsGlassFromOven(glass.glass);
		glass.state = glassState.sent;
		postConveyorReady = false;
		stateChanged();
		//glassList.remove(0);
	}
	
	void readyForNextGlass(myGlass glass){
		print("Removing this glass from list and calling for next glass");
		glassList.remove(0);
		prevConveyor.msgIamDoneToOven();
	}

	// ----- TRANSDUCER EVENTS -----

	@Override
	public void eventFired(TChannel channel, TEvent event, Object[] args) {
		switch(event){
		case OVEN:
			try{
				switch((Integer)args[0]){
				case 10:
					enterSensor = false;
					stateChanged();
					break;
				case 11:
					enterSensor = true;
					stateChanged();
					break;
				}
			}
			catch(Exception e){
				print(e.getMessage());
			}
			break;
		case C1: // C2's sensor acts both as C2's enterSensor and OvenAgent's exitSensor
			try {
				switch((Integer)args[0]) {
				case 10:
					exitSensor = false;
					stateChanged();
					break;
				case 11:
					exitSensor = true;
					stateChanged();
					break;
				}
			}
			catch(Exception e){
				print(e.getMessage());
			}
			break;
		}
			
	}
	
	// --- HACKS ---
	
	public void setPrevConveyor(Conveyor c) {
		this.prevConveyor = c; 
	}
	
	public void setPostConveyor(Conveyor c) {
		this.postConveyor = c;
	}

}
